package com.pokermind.operator.ai;

import org.springframework.stereotype.Component;
import java.util.*;

/**
 * GTO策略计算器
 * 提供基于博弈论的最优策略计算
 * 
 * 核心功能：
 * 1. 混合策略频率计算
 * 2. 平衡范围分析
 * 3. 防守频率计算
 * 4. 最小防守频率 (MDF)
 * 5. 剥削性调整建议
 * 
 * @author PokerMind Team
 */
@Component
public class GTOCalculator {

    /**
     * GTO分析结果
     */
    public static class GTOAnalysisResult {
        private double optimalCallFrequency;      // 最优跟注频率
        private double optimalFoldFrequency;      // 最优弃牌频率
        private double optimalBetFrequency;       // 最优下注频率
        private double minimumDefenseFrequency;   // 最小防守频率 (MDF)
        private Map<String, Double> actionFrequencies; // 各行动的GTO频率
        private double exploitabilityIndex;       // 可剥削性指数 (0-1)
        private String recommendedStrategy;       // 推荐策略类型
        private List<String> gtoReasons;         // GTO推理依据
        
        // Getters and Setters
        public double getOptimalCallFrequency() { return optimalCallFrequency; }
        public void setOptimalCallFrequency(double optimalCallFrequency) { this.optimalCallFrequency = optimalCallFrequency; }
        
        public double getOptimalFoldFrequency() { return optimalFoldFrequency; }
        public void setOptimalFoldFrequency(double optimalFoldFrequency) { this.optimalFoldFrequency = optimalFoldFrequency; }
        
        public double getOptimalBetFrequency() { return optimalBetFrequency; }
        public void setOptimalBetFrequency(double optimalBetFrequency) { this.optimalBetFrequency = optimalBetFrequency; }
        
        public double getMinimumDefenseFrequency() { return minimumDefenseFrequency; }
        public void setMinimumDefenseFrequency(double minimumDefenseFrequency) { this.minimumDefenseFrequency = minimumDefenseFrequency; }
        
        public Map<String, Double> getActionFrequencies() { return actionFrequencies; }
        public void setActionFrequencies(Map<String, Double> actionFrequencies) { this.actionFrequencies = actionFrequencies; }
        
        public double getExploitabilityIndex() { return exploitabilityIndex; }
        public void setExploitabilityIndex(double exploitabilityIndex) { this.exploitabilityIndex = exploitabilityIndex; }
        
        public String getRecommendedStrategy() { return recommendedStrategy; }
        public void setRecommendedStrategy(String recommendedStrategy) { this.recommendedStrategy = recommendedStrategy; }
        
        public List<String> getGtoReasons() { return gtoReasons; }
        public void setGtoReasons(List<String> gtoReasons) { this.gtoReasons = gtoReasons; }
    }

    /**
     * 手牌范围定义
     */
    public enum HandRange {
        PREMIUM("AA-QQ,AK", 0.045),      // 顶级手牌 4.5%
        STRONG("JJ-99,AQ,KQ", 0.08),     // 强牌 8%
        MEDIUM("88-22,AT,KJ,QJ", 0.15),  // 中等牌 15%
        WEAK("A2-A9,K2-KT,Q2-QT", 0.25), // 弱牌 25%
        BLUFF("suited connectors", 0.10); // 诈唬牌 10%
        
        private final String description;
        private final double frequency;
        
        HandRange(String description, double frequency) {
            this.description = description;
            this.frequency = frequency;
        }
        
        public String getDescription() { return description; }
        public double getFrequency() { return frequency; }
    }

    /**
     * 计算最小防守频率 (Minimum Defense Frequency)
     * 
     * 公式: MDF = bet_size / (pot + bet_size)
     * 
     * @param potSize 底池大小
     * @param betSize 下注大小
     * @return 最小防守频率 (0-1)
     */
    public double calculateMinimumDefenseFrequency(double potSize, double betSize) {
        if (potSize <= 0 || betSize <= 0) {
            return 0.0;
        }
        
        double mdf = betSize / (potSize + betSize);
        return Math.min(1.0, Math.max(0.0, mdf));
    }

    /**
     * 计算GTO混合策略频率
     * 
     * @param handStrength 手牌强度 (0-1)
     * @param potOdds 底池赔率
     * @param position 位置优势 (-1到1, -1最差, 1最好)
     * @param opponentCount 对手数量
     * @return GTO分析结果
     */
    public GTOAnalysisResult calculateGTOStrategy(double handStrength, double potOdds, 
                                                double position, int opponentCount) {
        GTOAnalysisResult result = new GTOAnalysisResult();
        List<String> reasons = new ArrayList<>();
        
        // 1. 基础手牌强度调整
        double adjustedStrength = adjustHandStrengthByOpponents(handStrength, opponentCount);
        reasons.add(String.format("手牌强度 %.2f 调整为 %.2f (对手数: %d)", 
                                handStrength, adjustedStrength, opponentCount));
        
        // 2. 位置调整
        double positionMultiplier = 1.0 + (position * 0.15); // 位置影响±15%
        adjustedStrength *= positionMultiplier;
        reasons.add(String.format("位置调整系数: %.2f (位置值: %.1f)", 
                                positionMultiplier, position));
        
        // 3. 计算各行动频率
        Map<String, Double> frequencies = calculateActionFrequencies(adjustedStrength, potOdds);
        result.setActionFrequencies(frequencies);
        
        // 4. 设置基础频率
        result.setOptimalCallFrequency(frequencies.getOrDefault("call", 0.0));
        result.setOptimalFoldFrequency(frequencies.getOrDefault("fold", 0.0));
        result.setOptimalBetFrequency(frequencies.getOrDefault("bet", 0.0));
        
        // 5. 计算最小防守频率
        double mdf = calculateMinimumDefenseFrequency(1.0, potOdds);
        result.setMinimumDefenseFrequency(mdf);
        reasons.add(String.format("最小防守频率: %.1f%% (底池赔率: %.2f)", 
                                mdf * 100, potOdds));
        
        // 6. 可剥削性评估
        double exploitability = calculateExploitability(adjustedStrength, frequencies);
        result.setExploitabilityIndex(exploitability);
        
        // 7. 策略推荐
        String strategy = determineStrategy(adjustedStrength, frequencies, exploitability);
        result.setRecommendedStrategy(strategy);
        reasons.add("策略类型: " + strategy);
        
        result.setGtoReasons(reasons);
        return result;
    }

    /**
     * 根据对手数量调整手牌强度
     */
    private double adjustHandStrengthByOpponents(double baseStrength, int opponents) {
        // 对手越多，手牌相对强度越弱
        double adjustment = Math.pow(0.85, opponents - 1);
        return baseStrength * adjustment;
    }

    /**
     * 计算各行动的GTO频率
     */
    private Map<String, Double> calculateActionFrequencies(double handStrength, double potOdds) {
        Map<String, Double> frequencies = new HashMap<>();
        
        if (handStrength >= 0.8) {
            // 超强牌：主要价值下注/加注
            frequencies.put("bet", 0.85);
            frequencies.put("call", 0.90);
            frequencies.put("fold", 0.02);
            frequencies.put("raise", 0.80);
        } else if (handStrength >= 0.6) {
            // 强牌：混合策略，倾向于价值
            frequencies.put("bet", 0.65);
            frequencies.put("call", 0.75);
            frequencies.put("fold", 0.15);
            frequencies.put("raise", 0.45);
        } else if (handStrength >= 0.4) {
            // 中等牌：保守策略，根据赔率决定
            boolean correctOdds = handStrength > potOdds;
            frequencies.put("bet", correctOdds ? 0.35 : 0.15);
            frequencies.put("call", correctOdds ? 0.60 : 0.35);
            frequencies.put("fold", correctOdds ? 0.30 : 0.65);
            frequencies.put("raise", 0.10);
        } else if (handStrength >= 0.2) {
            // 弱牌：偶尔诈唬，主要弃牌
            frequencies.put("bet", 0.12); // 偶尔诈唬
            frequencies.put("call", 0.20);
            frequencies.put("fold", 0.80);
            frequencies.put("raise", 0.05);
        } else {
            // 极弱牌：几乎总是弃牌
            frequencies.put("bet", 0.05);
            frequencies.put("call", 0.10);
            frequencies.put("fold", 0.90);
            frequencies.put("raise", 0.02);
        }
        
        return frequencies;
    }

    /**
     * 计算策略的可剥削性
     */
    private double calculateExploitability(double handStrength, Map<String, Double> frequencies) {
        // 检查策略是否过于极端
        double maxFreq = frequencies.values().stream().mapToDouble(Double::doubleValue).max().orElse(0.0);
        double minFreq = frequencies.values().stream().mapToDouble(Double::doubleValue).min().orElse(1.0);
        
        // 频率越极端，可剥削性越高
        double frequencySpread = maxFreq - minFreq;
        
        // 手牌强度与行动不匹配的惩罚
        double callFreq = frequencies.getOrDefault("call", 0.0);
        double mismatchPenalty = Math.abs(handStrength - callFreq) * 0.3;
        
        double exploitability = frequencySpread * 0.7 + mismatchPenalty;
        return Math.min(1.0, exploitability);
    }

    /**
     * 确定推荐策略类型
     */
    private String determineStrategy(double handStrength, Map<String, Double> frequencies, 
                                   double exploitability) {
        if (exploitability < 0.15) {
            return "GTO_OPTIMAL";
        } else if (handStrength > 0.7) {
            return "VALUE_HEAVY";
        } else if (handStrength < 0.3) {
            return "BLUFF_CANDIDATE";
        } else {
            return "MIXED_STRATEGY";
        }
    }

    /**
     * 计算翻前开牌范围
     * 
     * @param position 位置 (UTG=0, CO=0.7, BTN=1.0)
     * @param stackDepth 筹码深度 (BB数量)
     * @param opponentCount 对手数量
     * @return 推荐开牌频率
     */
    public double calculatePreflopRange(double position, int stackDepth, int opponentCount) {
        // 基础范围 (UTG约8%, BTN约45%)
        double baseRange = 0.08 + (position * 0.37);
        
        // 筹码深度调整
        double stackMultiplier = stackDepth > 100 ? 1.1 : (stackDepth < 30 ? 0.85 : 1.0);
        
        // 对手数量调整
        double opponentMultiplier = Math.pow(0.92, Math.max(0, opponentCount - 1));
        
        double finalRange = baseRange * stackMultiplier * opponentMultiplier;
        return Math.min(0.5, Math.max(0.05, finalRange));
    }

    /**
     * 计算持续下注频率
     * 
     * @param boardTexture 牌面结构 (DRY=0.3, WET=0.8, CONNECTED=1.0)
     * @param rangeAdvantage 范围优势 (-1到1)
     * @param position 位置优势
     * @return 推荐持续下注频率
     */
    public double calculateContinuationBetFrequency(double boardTexture, double rangeAdvantage, 
                                                  double position) {
        // 基础持续下注频率 65%
        double baseCbet = 0.65;
        
        // 牌面结构调整：干燥牌面更多cbet
        double textureAdjust = (1.0 - boardTexture) * 0.15;
        
        // 范围优势调整
        double rangeAdjust = rangeAdvantage * 0.2;
        
        // 位置调整
        double positionAdjust = position * 0.1;
        
        double finalCbet = baseCbet + textureAdjust + rangeAdjust + positionAdjust;
        return Math.min(0.85, Math.max(0.45, finalCbet));
    }
}
